/* Declarations for the memory interface.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_MEMORY__
#define __KP_MEMORY__   1

#include "interp.hpp"
#include "initop.hpp"

KP_DECLS_BEGIN

/* Allocate a block of SIZE bytes, aligned for any type necessary.
 * Does NOT register the block (i.e: It wont' be visible to the GC). */
KP_EXPORT void* xmalloc (size_t size);

/* Reallocate a memory block for NSIZE bytes. Like above, this function
 * won't make the resulting block GC-visible. */
KP_EXPORT void* xrealloc (void *ptr, size_t nsize);

/* Allocate a varobject, given SIZE, TYPE and MASK, filling the
 * header with such data. Does not register the header. */
KP_EXPORT void* alloch (size_t size, int type, int mask = TYPE_SHIFT);

template <typename T>
T* alloch (int mask = TYPE_SHIFT)
{
  return ((T *)alloch (sizeof (T), T::code, mask));
}

// Deallocate memory for PTR. Does not unregister the object.
KP_EXPORT void xfree (void *ptr);

// Register a varobj of SIZE bytes, thus making it visible to the GC.
KP_EXPORT void gc_register (interpreter *interp, varobj *ptr, size_t size);

template <typename T>
void gc_register (interpreter *interp, T *ptr)
{
  gc_register (interp, ptr, sizeof (*ptr));
}

// Handler for the GC request event.
KP_EXPORT result<object> gcreq_handler (interpreter *interp,
                                        object *argv, int argc);

// Allocate a memory manager.
KP_EXPORT memmgr* memmgr_alloc ();

// Free a memory manager.
KP_EXPORT void memmgr_free (memmgr *mgrp);

// Raise an out-of-memory error.
[[noreturn]] KP_EXPORT void raise_oom (interpreter *interp = nullptr);

// Acquire the GC lock.
KP_EXPORT void gc_lock (interpreter *interp);

// Get a reference to the GC status (enabled or disabled).
KP_EXPORT bool& gc_status ();

inline bool
gc_enable ()
{
  bool ret = gc_status ();
  gc_status() = true;
  return (ret);
}

inline bool
gc_disable ()
{
  bool ret = gc_status ();
  gc_status() = false;
  return (ret);
}

struct gc_guard
{
  bool prev;

  gc_guard () : prev (gc_disable ())
    {
    }

  ~gc_guard ()
    {
      if (this->prev)
        gc_enable ();
    }
};

// Release the GC lock.
KP_EXPORT void gc_unlock ();

// Perform a garbage collection. Collect all generations if FULL is true.
KP_EXPORT result<void> gc (bool full);

// Generate a write barrier for object OBJ contained in PARENT.
KP_EXPORT result<void> gc_wbarrier (interpreter *interp,
                                    object parent, object obj);

// Initialize the memory subsystem.
KP_EXPORT bool memory_init ();

// Call finalizers before exiting.
KP_EXPORT void memory_exit ();

KP_DECLS_END

#endif
